#include <graphics/Light.h>

namespace ne
{
    Light::Light()
        : Node(),
          mType(LT_POINT),
          mAmbient(Color(0.0f, 0.0f, 0.0f, 1.0f)),
          mDiffuse(Color(1.0f, 1.0f, 1.0f, 1.0f)),
          mSpecular(Color(1.0f, 1.0f, 1.0f, 1.0f)),
          mSpotExponent(0.0f),
          mSpotCutOffAngle(Math::HALF_CIRCLE),
          mAttenuationConstant(1.0f),
          mAttenuationLinear(0.0f),
          mAttenuationQuadratic(0.0f)
    {
    }

    void Light::setScale(const Vector3 &v)
    {
    }

    void Light::setType(const LightType &lt)
    {
        mType = lt;
        if (lt == LT_SPOTLIGHT)
        {
            Math::Clamp(mSpotCutOffAngle, 0.0f, Math::QUARTER_CIRCLE);
        }
        else
        {
            mSpotCutOffAngle = Math::HALF_CIRCLE;
        }
    }

    LightType Light::getType() const
    {
        return mType;
    }

    void Light::setAmbient(const Color &a)
    {
        mAmbient = a;
    }

    Color Light::getAmbient() const
    {
        return mAmbient;
    }

    void Light::setDiffuse(const Color &a)
    {
        mDiffuse = a;
    }

    Color Light::getDiffuse() const
    {
        return mDiffuse;
    }

    void Light::setSpecular(const Color &a)
    {
        mSpecular = a;
    }

    Color Light::getSpecular() const
    {
        return mSpecular;
    }

    void Light::setSpotExponent(const real a)
    {
        if (mType == LT_SPOTLIGHT)
        {
            mSpotExponent = Math::Clamp(a, 0.0f, 128.f);
        }
    }

    real Light::getSpotExponent() const
    {
        return mSpotExponent;
    }

    // [0.0f, 90.0f] and [180.0f]
    void Light::setSpotCutOffAngle(const real a)
    {
        if (mType == LT_SPOTLIGHT)
        {
            mSpotCutOffAngle = Math::Clamp(a, 0.0f, Math::QUARTER_CIRCLE);
        }
        else
        {
            mSpotCutOffAngle = Math::HALF_CIRCLE;
        }
    }

    real Light::getSpotCutOffAngle() const
    {
        return mSpotExponent;
    }

    void Light::setAttenuationFactor(const LightAttenuationType at, const real a)
    {
        if (a >= 0.0f)
        {
            switch (at)
            {
                case LAT_CONSTANT:
                    mAttenuationConstant = a;
                    break;

                case LAT_LINEAR:
                    mAttenuationLinear = a;
                    break;

                case LAT_QUADRATIC:
                    mAttenuationQuadratic = a;
                    break;

                default:
                    break;
            }
        }
    }

    real Light::getAttenuationFactor(const LightAttenuationType at) const
    {
        switch (at)
        {
            case LAT_CONSTANT:
                return mAttenuationConstant;

            case LAT_LINEAR:
                return mAttenuationLinear;

            case LAT_QUADRATIC:
                return mAttenuationQuadratic;

            default:
                return 0.0f;
        }
    }

    void Light::useLight(const bool use, const uint32_t index)
    {
#if NE_RENDER_SYSTEM == NE_RENDER_SYSTEM_OPENGL
        GLenum glIndex = GL_LIGHT0 + index;
        if (use)
        {
            glLightfv(glIndex, GL_AMBIENT, mAmbient.ptr());
            glLightfv(glIndex, GL_DIFFUSE, mDiffuse.ptr());
            glLightfv(glIndex, GL_SPECULAR, mSpecular.ptr());

            // Set position
            Vector4 position(getPosition(TS_WORLD));
            glLightfv(glIndex, GL_POSITION, position.ptr());

            // Set Cut off angle
            glLightf(glIndex, GL_SPOT_CUTOFF, mSpotCutOffAngle);

            // Set Spotlight
            if (mType == LT_SPOTLIGHT)
            {
                glLightf(glIndex, GL_SPOT_EXPONENT, mSpotExponent);
                // Set directon
                Vector4 directon(getDirection());
                glLightfv(glIndex, GL_SPOT_DIRECTION, directon.ptr());
            }

            // Set Attenuation
            glLightf(glIndex, GL_CONSTANT_ATTENUATION, mAttenuationConstant);
			glLightf(glIndex, GL_LINEAR_ATTENUATION, mAttenuationLinear);
			glLightf(glIndex, GL_QUADRATIC_ATTENUATION, mAttenuationQuadratic);

            glEnable(glIndex);
        }
        else
        {
            glDisable(glIndex);
        }
#endif
    }

    const Matrix4& Light::getViewMatrix() const
    {
        return getInverseTransformMatrix();
    }

    Light::~Light()
    {
    }
}

